/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef OHOS_GRPC_NODE_HTTP2_UTILS_H
#define OHOS_GRPC_NODE_HTTP2_UTILS_H
#include "http2_map.h"
#include "http2_struct.h"
#include "nlohmann/json_fwd.hpp"
#include "ohos_log.h"
#include <arpa/inet.h>
#include <netdb.h>
#include <string>
#include <sys/socket.h>
#include "securec.h"
#include "nlohmann/json.hpp"
#include "http2_constant.h"
#include <sstream>
#include <nghttp2/nghttp2.h>
#include "http2_constant.h"
using namespace std;

std::vector<string> ResolveHostTxt(const string &domain)
{
    std::vector<std::string> txtRecords;
    struct addrinfo hints;
    struct addrinfo *res;
    struct addrinfo *p;
    int status;
    memset_s(&hints, sizeof hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    if ((status = getaddrinfo(domain.c_str(), nullptr, &hints, &res)) != 0) {
        LOGE("getaddrinfo error:%s", gai_strerror(status));
        return txtRecords;
    }
    for (p = res; p != nullptr; p = p->ai_next) {
        char host[NI_MAXHOST];
        if (getnameinfo(p->ai_addr, p->ai_addrlen, host, sizeof host, nullptr, 0, NI_NAMEREQD) == 0) {
            LOGE("resolveTxt host %s", host);
            txtRecords.push_back(host);
        }
    }
    freeaddrinfo(res);
    return txtRecords;
}

string FindIPAddr(const string &hostname)
{
    struct addrinfo hints;
    struct addrinfo *resultAddr;
    int status;
    memset_s(&hints, sizeof hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    if ((status = getaddrinfo(hostname.c_str(), nullptr, &hints, &resultAddr)) != 0) {
        LOGE("getaddrinfo error:%s", gai_strerror(status));
        return "";
    }
    std::unique_ptr<struct addrinfo, decltype(&freeaddrinfo)> resultAddrPtr(resultAddr, freeaddrinfo);
    std::string ipv4Addr = "";
    for (struct addrinfo *model = resultAddrPtr.get(); model != nullptr; model = model->ai_next) {
        if (model->ai_family == AF_INET) {
            struct sockaddr_in *ipv4 = reinterpret_cast<struct sockaddr_in*>(model->ai_addr);
            char ipstr[INET_ADDRSTRLEN];
            if (inet_ntop(AF_INET, &(ipv4->sin_addr), ipstr, sizeof(ipstr))) {
                ipv4Addr = ipstr;
            }
        }
    }
    if (!ipv4Addr.empty()) {
        LOGE("FindIPAddr ip %s", ipv4Addr.c_str());
    }
    return ipv4Addr;
}

int TimeDifference(time_t timestamp1, time_t timestamp2)
{
    return std::abs(timestamp1 - timestamp2);
}

string UnorderedMapToJsonString(const ConcurrentHashMap<string, string> &myMap)
{
    nlohmann::json jsonMap;
    myMap.ForEach([&](const string& key, const string& value) {
        jsonMap[key] = value;
    });
    return jsonMap.dump();
}

bool CreateArrayBuffer(napi_env env, void *src, size_t srcLen, napi_value *res)
{
    if (src == nullptr || srcLen == 0) {
        return false;
    }
    void *nativePtr = nullptr;
    if (napi_create_arraybuffer(env, srcLen, &nativePtr, res) != napi_ok || nativePtr == nullptr) {
        return false;
    }
    std::copy((uint8_t *)src, (uint8_t *)src + srcLen, (uint8_t *)nativePtr);
    return true;
}

RequestTask ParseDomain(string domain)
{
    size_t pos = domain.find(':');
    RequestTask requestTask;
    if (pos != std::string::npos) {
        std::string host = domain.substr(0, pos);
        std::string port = domain.substr(pos + 1);
        requestTask.host =  host;
        requestTask.port =  port;
    }
    return requestTask;
}

const unordered_map<string, string> ParseReqExtendParams(string extendParams)
{
    unordered_map<string, string> resultMap;
    try {
        if (extendParams.size() <= 0) {
            return resultMap;
        }
        nlohmann::json jsonResult = nlohmann::json::parse(extendParams);
        for (auto it = jsonResult.begin(); it != jsonResult.end(); ++it) {
            LOGE("HandleRequestParams extendParams k %s v %s", it.key().c_str(), it.value().dump().c_str());
            std::string paramKey = it.key();
            if (paramKey == ":authority" || paramKey == ":method" || paramKey == ":scheme"
                || paramKey == "accept-encoding" || paramKey == "content-type" || paramKey == "grpc-accept-encoding"
                || paramKey == "method" || paramKey == "te" || paramKey ==":path" || paramKey == "user-agent") {
            } else {
                resultMap[paramKey] = it.value().dump();
            }
        }
    } catch (const nlohmann::json::parse_error& e) {
        LOGE("HandleRequestParams JSON parsing error: %s", e.what());
    }
    return resultMap;
}

bool CheckStringIsArray(const string str)
{
    if (str.empty() || str.front() != '[' || str.back() != ']') {
        return false;
    }
    try {
        nlohmann::json j = nlohmann::json::parse(str);
        return j.is_array();
    } catch (...) {
        return false;
    }
}

string GetFirstIndexFromArray(const string str)
{
    try {
        if (str.size() >= NUM_2 && str.front() == '[' && str.back() == ']') {
            nlohmann::json j = nlohmann::json::parse(str);
            if (!j.empty() && j.is_array() && j[0].is_string()) {
                return j[0].get<std::string>();
            }
        }
    } catch (const std::exception& e) {
        LOGE("GetFirstIndexFromArray JSON parsing error: %s", e.what());
    }
    return str;
}

nghttp2_nv CreateNV(const char* name, const char* value)
{
    nghttp2_nv nv;
    nv.namelen = strlen(name);
    nv.valuelen = strlen(value);
    nv.name = new uint8_t[nv.namelen + 1];
    nv.value = new uint8_t[nv.valuelen + 1];
    strcpy_s(reinterpret_cast<char*>(nv.name), nv.namelen + 1, name);
    strcpy_s(reinterpret_cast<char*>(nv.value), nv.valuelen + 1, value);
    nv.flags = 0;
    return nv;
}

bool CheckReqId(const std::string* reqId)
{
    if (reqId == nullptr) {
        LOGE("Error CheckReqId nullptr");
        return false;
    }
    if (reqId->empty()) {
        LOGE("Error CheckReqId empty");
        return false;
    }
    if (reqId->length() != NUM_36) {
        LOGE("Error CheckReqId length");
        return false;
    }
    for (char c : *reqId) {
        if (!((std::islower(c) || std::isdigit(c) || c == '-'))) {
            return false;
        }
    }
    return true;
}

vector<nghttp2_nv> RequestHeader(string host, string path)
{
    std::vector<nghttp2_nv> nva;
    nva.push_back(CreateNV(":method", "POST"));
    nva.push_back(CreateNV(":path", path.c_str()));
    nva.push_back(CreateNV(":scheme", "https"));
    nva.push_back(CreateNV(":authority", host.c_str()));
    nva.push_back(CreateNV("content-type", "application/grpc"));
    nva.push_back(CreateNV("accept", "*/*"));
    nva.push_back(CreateNV("te", "trailers"));
    nva.push_back(CreateNV("accept-encoding", "identity"));
    nva.push_back(CreateNV("grpc-accept-encoding", "identity,deflate,gzip"));
    nva.push_back(CreateNV("user-agent", "grpc-node-js/0.1.0" NGHTTP2_VERSION));
    return nva;
}
#endif // OHOS_GRPC_NODE_HTTP2_UTILS_H
